package com.chap8;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.locks.StampedLock;

/**
 * <p>
 * <code>StampedLockTest</code>
 * </p>
 *
 * @author junbao3
 * TODO
 * @date 2023-03-24 10:36
 * Copyright (C) 2021 IFlyTek. All rights reserved.
 */
public class StampedLockTest {
    public static void main(String[] args) throws InterruptedException {
        Point point = new Point();
        new Thread(()->point.move(100,200)).start();
        Thread.sleep(100);
        new Thread(()->point.distanceFromOrigin()).start();
        Thread.sleep(500);

        //distanceFromOrigin休眠2秒的时候没有写操作
//        new Thread(()->point.move(300,400)).start();
        /*
        11:11:46.418 [Thread-0] DEBUG com.chap8.Point - 获取到写锁
        11:11:46.421 [Thread-0] DEBUG com.chap8.Point - 释放写锁
        11:11:46.518 [Thread-1] DEBUG com.chap8.Point - 第一次读，x:100.0,y:200.0,currentX:100.0
        11:11:48.520 [Thread-1] DEBUG com.chap8.Point - 第二次读，x:100.0,y:200.0,currentY:200.0
        */

        //distanceFromOrigin休眠2秒的时候有写操作
        new Thread(()->point.move(300,400)).start();
    }
}

@Slf4j
class Point{
    private final StampedLock stampedLock = new StampedLock();

    private double x;
    private double y;

    public void move(double deltaX,double deltaY){
        //获取写锁
        long stamp = stampedLock.writeLock();
        log.debug("获取到写锁");
        try {
            x+=deltaX;
            y+=deltaY;
        }finally {
            stampedLock.unlockWrite(stamp);
            log.debug("释放写锁");
        }
    }

    public double distanceFromOrigin(){
        //获得一个乐观读锁,无锁的
        long stamp = stampedLock.tryOptimisticRead();
        double currentX= x;
        //x可能和currentX不一样
        log.debug("第一次读，x:{},y:{},currentX:{}",x,y,currentX);
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        double currentY = y;
        log.debug("第二次读，x:{},y:{},currentY:{}",x,y,currentY);

        //检查乐观锁读锁后是否有其他写锁发生
        if (!stampedLock.validate(stamp)){//有写锁 返回false
            //获取悲观读锁
            stamp = stampedLock.readLock();
            try{
                currentX = x;
                currentY = y;
                log.debug("最终结果，x:{},y:{},currentX:{},currentY:{}",x,y,currentX,currentY);
            }finally {
                stampedLock.unlockRead(stamp);
            }

        }

        return Math.sqrt(currentX*currentX+currentY*currentY);

    }
}
